package com.wechat.service.impl;

import com.wechat.entity.config.AppConfig;
import com.wechat.entity.constants.Constants;
import com.wechat.entity.dto.MessageSendDto;
import com.wechat.entity.dto.SysSettingDto;
import com.wechat.entity.dto.TokenUserInfoDto;
import com.wechat.entity.enums.*;
import com.wechat.entity.po.ChatMessage;
import com.wechat.entity.po.ChatSession;
import com.wechat.entity.po.UserContact;
import com.wechat.entity.query.ChatMessageQuery;
import com.wechat.entity.query.ChatSessionQuery;
import com.wechat.entity.query.SimplePage;
import com.wechat.entity.query.UserContactQuery;
import com.wechat.entity.vo.PaginationResultVO;
import com.wechat.exception.BusinessException;
import com.wechat.mappers.ChatMessageMapper;
import com.wechat.mappers.ChatSessionMapper;
import com.wechat.mappers.UserContactMapper;
import com.wechat.redis.RedisComponent;
import com.wechat.service.ChatMessageService;
import com.wechat.utils.DateUtil;
import com.wechat.utils.StringTools;
import com.wechat.websocket.MessageHandler;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.util.Date;
import java.util.List;


/**
 * 聊天消息表 业务接口实现
 */
@Service("chatMessageService")
public class ChatMessageServiceImpl implements ChatMessageService {

    private static final Logger logger = LoggerFactory.getLogger(ChatMessageServiceImpl.class);

    @Resource
    private ChatMessageMapper<ChatMessage, ChatMessageQuery> chatMessageMapper;

    @Resource
    private ChatSessionMapper<ChatSession, ChatSessionQuery> chatSessionMapper;

    @Resource
    private MessageHandler messageHandler;

    @Resource
    private AppConfig appConfig;

    @Resource
    private UserContactMapper<UserContact, UserContactQuery> userContactMapper;

    @Resource
    private RedisComponent redisComponent;

    /**
     * 根据条件查询列表
     */
    @Override
    public List<ChatMessage> findListByParam(ChatMessageQuery param) {
        return this.chatMessageMapper.selectList(param);
    }

    /**
     * 根据条件查询列表
     */
    @Override
    public Integer findCountByParam(ChatMessageQuery param) {
        return this.chatMessageMapper.selectCount(param);
    }

    /**
     * 分页查询方法
     */
    @Override
    public PaginationResultVO<ChatMessage> findListByPage(ChatMessageQuery param) {
        int count = this.findCountByParam(param);
        int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

        SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
        param.setSimplePage(page);
        List<ChatMessage> list = this.findListByParam(param);
        PaginationResultVO<ChatMessage> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
        return result;
    }

    /**
     * 新增
     */
    @Override
    public Integer add(ChatMessage bean) {
        return this.chatMessageMapper.insert(bean);
    }

    /**
     * 批量新增
     */
    @Override
    public Integer addBatch(List<ChatMessage> listBean) {
        if (listBean == null || listBean.isEmpty()) {
            return 0;
        }
        return this.chatMessageMapper.insertBatch(listBean);
    }

    /**
     * 批量新增或者修改
     */
    @Override
    public Integer addOrUpdateBatch(List<ChatMessage> listBean) {
        if (listBean == null || listBean.isEmpty()) {
            return 0;
        }
        return this.chatMessageMapper.insertOrUpdateBatch(listBean);
    }

    /**
     * 多条件更新
     */
    @Override
    public Integer updateByParam(ChatMessage bean, ChatMessageQuery param) {
        StringTools.checkParam(param);
        return this.chatMessageMapper.updateByParam(bean, param);
    }

    /**
     * 多条件删除
     */
    @Override
    public Integer deleteByParam(ChatMessageQuery param) {
        StringTools.checkParam(param);
        return this.chatMessageMapper.deleteByParam(param);
    }

    /**
     * 根据MessageId获取对象
     */
    @Override
    public ChatMessage getChatMessageByMessageId(Long messageId) {
        return this.chatMessageMapper.selectByMessageId(messageId);
    }

    /**
     * 根据MessageId修改
     */
    @Override
    public Integer updateChatMessageByMessageId(ChatMessage bean, Long messageId) {
        return this.chatMessageMapper.updateByMessageId(bean, messageId);
    }

    /**
     * 根据MessageId删除
     */
    @Override
    public Integer deleteChatMessageByMessageId(Long messageId) {
        return this.chatMessageMapper.deleteByMessageId(messageId);
    }


    /**
     * 保存+发送聊天消息
     * @param chatMessage---封装的聊天相关消息
     * @param tokenUserInfoDto----获取发送用户信息
     */
    @Override
    public MessageSendDto saveMessage(ChatMessage chatMessage, TokenUserInfoDto tokenUserInfoDto) {
        //不是机器人回复，判断好友状态
        if (!Constants.ROBOT_UID.equals(tokenUserInfoDto.getUserId())) {
            List<String> contactList = redisComponent.getUserContactList(tokenUserInfoDto.getUserId());//获取好友列表
            if (!contactList.contains(chatMessage.getContactId())) {//非好友状态
                UserContactTypeEnum userContactTypeEnum = UserContactTypeEnum.getByPrefix(chatMessage.getContactId());
                if (UserContactTypeEnum.USER == userContactTypeEnum) {//用户非好友
                    throw new BusinessException(ResponseCodeEnum.CODE_902);
                } else {//群组非好友
                    throw new BusinessException(ResponseCodeEnum.CODE_903);
                }
            }
        }

        String sessionId = null;
        String sendUserId = tokenUserInfoDto.getUserId();
        String contactId = chatMessage.getContactId();
        UserContactTypeEnum userContactTypeEnum = UserContactTypeEnum.getByPrefix(contactId);
        //设置发送时间
        Long curTime = System.currentTimeMillis();
        //获取会话Id
        if(UserContactTypeEnum.USER == userContactTypeEnum){
            sessionId = StringTools.getChatSessionId4User(new String[]{sendUserId, contactId});
        }else {
            sessionId = StringTools.getChatSessionId4Group(contactId);
        }
        MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByType(chatMessage.getMessageType());
        //判断消息类型：只能接收媒体文件和普通聊天消息
        if(messageTypeEnum == null || !ArrayUtils.contains(new Integer[]{MessageTypeEnum.MEDIA_CHAT.getType(),MessageTypeEnum.CHAT.getType()}, chatMessage.getMessageType())){
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }

        //发送信息类型:媒体消息---正在发送，普通聊天消息---已发送
        Integer status = MessageTypeEnum.MEDIA_CHAT == messageTypeEnum ? MessageStatusEnum.SENDING.getStatus() : MessageStatusEnum.SENDED.getStatus();
        //聊天内容:对内容进行转义
        String messageContent = StringTools.cleanHtmlTag(chatMessage.getMessageContent());
        //封装聊天的相关信息
        chatMessage.setSendTime(curTime);
        chatMessage.setSessionId(sessionId);
        chatMessage.setStatus(status);
        chatMessage.setMessageContent(messageContent);

        //更新会话信息
        ChatSession chatSession = new ChatSession();
        chatSession.setLastMessage(messageContent);
        if(UserContactTypeEnum.GROUP == userContactTypeEnum){//群组内的聊天需要显示昵称
            chatSession.setLastMessage(tokenUserInfoDto.getNickName()+":"+messageContent);
        }
        chatSession.setLastReceiveTime(curTime);
        chatSessionMapper.updateBySessionId(chatSession, sessionId);

        //添加聊天消息
        chatMessage.setSendUserId(sendUserId);
        chatMessage.setSendUserNickName(tokenUserInfoDto.getNickName());
        chatMessage.setContactType(userContactTypeEnum.getType());
        chatMessageMapper.insert(chatMessage);//保存聊天消息

        //发送消息
        MessageSendDto messageSendDto = new MessageSendDto<>();
        BeanUtils.copyProperties(chatMessage, messageSendDto);

        //如果是与机器人聊天,这里有个身份转换，首先我发消息给机器人，之后机器人会发送信息给我，这里需要再次调用saveMessage方法
        if(Constants.ROBOT_UID.equals(chatMessage.getContactId())){
            SysSettingDto sysSettingDto = redisComponent.getSysSetting();
            TokenUserInfoDto robot = new TokenUserInfoDto();
            robot.setUserId(Constants.ROBOT_UID);
            robot.setNickName(sysSettingDto.getRobotNickName());
            ChatMessage robotMessage = new ChatMessage();
            robotMessage.setContactId(sendUserId);//设置机器人的联系人是发送用户的id
            //机器人回复---对接ai接口
            robotMessage.setMessageContent("ai聊天");
            robotMessage.setMessageType(MessageTypeEnum.MEDIA_CHAT.getType());
            saveMessage(robotMessage, robot);
        }else {
            messageHandler.sendMessage(messageSendDto);
        }
        return messageSendDto;
    }

    /**
     * 上传文件
     * @param userId---用户Id
     * @param messageId---消息Id
     * @param file--- 文件
     * @param cover--- 封面
     */
    @Override
    public void uploadMessageFile(String userId, Long messageId, MultipartFile file, MultipartFile cover) {
        //校验，防止消息不存在/用户消息对应关系错误
        ChatMessage message = chatMessageMapper.selectByMessageId(messageId);
        if (null == message) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if (!message.getSendUserId().equals(userId)) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }

        SysSettingDto sysSettingDto = redisComponent.getSysSetting();
        String fileSuffix = StringTools.getFileSuffix(file.getOriginalFilename());//获取文件后缀
        if (StringTools.isEmpty(fileSuffix)) {//文件后缀为空
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        //后缀转小写统一判断
        String fileSuffixLowerCase = fileSuffix.toLowerCase();
        Long fileSize = file.getSize();
        //检查是否为图片文件
        if(ArrayUtils.contains(Constants.IMAGE_SUFFIX_LIST,fileSuffixLowerCase)){
            if(fileSize > Constants.FILE_SIZE_MB * sysSettingDto.getMaxImageSize()) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        }
        //检查是否为视频文件
        else if(ArrayUtils.contains(Constants.VIDEO_SUFFIX_LIST,fileSuffixLowerCase)){
            if(fileSize > Constants.FILE_SIZE_MB * sysSettingDto.getMaxVideoSize()){
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        }
        //检查其他类型文件
        else {
            if(fileSize > Constants.FILE_SIZE_MB * sysSettingDto.getMaxFileSize()){
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        }

        //对上传的文件重新命名
        String fileName = file.getOriginalFilename();
        String fileExtName = StringTools.getFileSuffix(fileName);
        String fileRealName = messageId + fileExtName;
        
        String month = DateUtil.format(new Date(message.getSendTime()), DateTimePatternEnum.YYYYMM.getPattern());
        File folder = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + month);//创建专属文件夹：文件保存路径/年月
        if (!folder.exists()) {
            folder.mkdirs();
        }

        File uploadFile = new File(folder.getPath() + "/" + fileRealName);//实际上传文件地址：文件保存路径/文件名
        try {
            file.transferTo(uploadFile);//保存文件
            if (cover != null) {
                cover.transferTo(new File(uploadFile.getPath() + Constants.COVER_IMAGE_SUFFIX));
            }
        } catch (Exception e) {
            logger.error("上传文件失败", e);
            throw new BusinessException("文件上传失败");
        }

        
        ChatMessage updateInfo = new ChatMessage();
        updateInfo.setStatus(MessageStatusEnum.SENDED.getStatus());
        ChatMessageQuery messageQuery = new ChatMessageQuery();
        messageQuery.setMessageId(messageId);
        chatMessageMapper.updateByParam(updateInfo, messageQuery);//更新文件上传状态

        MessageSendDto messageSend = new MessageSendDto();
        messageSend.setStatus(MessageStatusEnum.SENDED.getStatus());
        messageSend.setMessageId(message.getMessageId());
        messageSend.setMessageType(MessageTypeEnum.FILE_UPLOAD.getType());
        messageSend.setContactId(message.getContactId());
        messageHandler.sendMessage(messageSend);//发送文件消息
    }

    /**
     * 下载文件---处理的是聊天中的文件
     * @param userInfoDto---接收信息的用户信息
     * @param messageId---消息Id
     * @param cover---是否下载封面
     * @return
     */
    @Override
    public File downloadFile(TokenUserInfoDto userInfoDto, Long messageId, Boolean cover) {
        ChatMessage message = chatMessageMapper.selectByMessageId(messageId);
        String contactId = message.getContactId();
        UserContactTypeEnum contactTypeEnum = UserContactTypeEnum.getByPrefix(contactId);
        if (UserContactTypeEnum.USER.getType().equals(contactTypeEnum) && !userInfoDto.getUserId().equals(message.getContactId())) {//接收人非指定用户
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if (UserContactTypeEnum.GROUP.getType().equals(contactTypeEnum)) {//判断当前用户是否在群聊内
            UserContactQuery userContactQuery = new UserContactQuery();
            userContactQuery.setUserId(userInfoDto.getUserId());
            userContactQuery.setContactType(UserContactTypeEnum.GROUP.getType());
            userContactQuery.setContactId(contactId);
            userContactQuery.setStatus(UserContactStatusEnum.FRIEND.getStatus());
            Integer contactCount = userContactMapper.selectCount(userContactQuery);
            if (contactCount == 0) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        }

        //获取下载文件路径
        String month = DateUtil.format(new Date(message.getSendTime()), DateTimePatternEnum.YYYYMM.getPattern());
        File folder = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + month);
        if (!folder.exists()) {
            folder.mkdirs();
        }

        String fileName = message.getFileName();
        String fileExtName = StringTools.getFileSuffix(fileName);//获取文件后缀
        String fileRealName = messageId + fileExtName;//实际文件名

        if (cover != null && cover) {
            fileRealName = fileRealName + Constants.COVER_IMAGE_SUFFIX;
        }

        File file = new File(folder.getPath() + "/" + fileRealName);
        if (!file.exists()) {
            logger.info("文件不存在");
            throw new BusinessException(ResponseCodeEnum.CODE_602);
        }
        return file;
    }


//    /**
//     * 撤回消息
//     * @param messageId---消息Id
//     * @param tokenUserInfoDto---用户信息
//     * @return
//     */
//    @Override
//    public MessageSendDto withdrawMessage(Long messageId, TokenUserInfoDto tokenUserInfoDto) {
//        // 1. 查询原消息
//        ChatMessage originalMessage = chatMessageMapper.selectByMessageId(messageId);
//        if (originalMessage == null) {
//            throw new BusinessException( "消息不存在");
//        }
//
//        // 2. 权限验证：只能撤回自己的消息
//        if (!originalMessage.getSendUserId().equals(tokenUserInfoDto.getUserId())) {
//            throw new BusinessException("只能撤回自己的消息");
//        }
//
//        // 3. 时间验证：2分钟内可撤回
//        Long currentTime = System.currentTimeMillis();
//        Long sendTime = originalMessage.getSendTime();
//        if (currentTime - sendTime > 2 * 60 * 1000) { // 2分钟
//            throw new BusinessException( "消息已超过2分钟，无法撤回");
//        }
//
//        // 4. 更新原消息状态为已撤回
//        ChatMessage updateMessage = new ChatMessage();
//        updateMessage.setStatus(MessageStatusEnum.WITHDRAW.getStatus());
//        updateMessage.setWithdrawMessageId(messageId);
//        updateMessage.setIsWithdrawTip(true);
//        chatMessageMapper.updateByMessageId(updateMessage, messageId);
//
//        // 5. 创建撤回提示消息（可选，根据产品需求）
//        ChatMessage withdrawTipMessage = createWithdrawTipMessage(originalMessage, tokenUserInfoDto);
//        chatMessageMapper.insert(withdrawTipMessage);
//
//        // 6. 构建撤回通知DTO
//        MessageSendDto withdrawDto = buildWithdrawMessageDto(originalMessage, withdrawTipMessage, tokenUserInfoDto);
//
//        // 7. 广播撤回通知
//        messageHandler.sendMessage(withdrawDto);
//
//        return withdrawDto;
//    }
//
//    /**
//     * 创建撤回提示消息
//     */
//    private ChatMessage createWithdrawTipMessage(ChatMessage originalMessage, TokenUserInfoDto tokenUserInfoDto) {
//        String tipContent = "撤回了一条消息";
//        if (UserContactTypeEnum.GROUP.getType().equals(originalMessage.getContactType())) {
//            tipContent = tokenUserInfoDto.getNickName() + " " + tipContent;
//        }
//
//        return ChatMessage.builder()
//                .sessionId(originalMessage.getSessionId())
//                .messageType(MessageTypeEnum.WITHDRAW.getType())
//                .messageContent(tipContent)
//                .sendUserId(tokenUserInfoDto.getUserId())
//                .sendUserNickName(tokenUserInfoDto.getNickName())
//                .sendTime(System.currentTimeMillis())
//                .contactId(originalMessage.getContactId())
//                .contactType(originalMessage.getContactType())
//                .status(MessageStatusEnum.SENDED.getStatus())
//                .withdrawMessageId(originalMessage.getMessageId())
//                .isWithdrawTip(true)
//                .build();
//    }
//
//    /**
//     * 构建撤回消息DTO
//     */
//    private MessageSendDto buildWithdrawMessageDto(ChatMessage originalMessage,
//                                                   ChatMessage tipMessage,
//                                                   TokenUserInfoDto tokenUserInfoDto) {
//        MessageSendDto dto = new MessageSendDto();
//        dto.setMessageId(tipMessage.getMessageId());
//        dto.setSessionId(originalMessage.getSessionId());
//        dto.setSendUserId(tokenUserInfoDto.getUserId());
//        dto.setSendUserNickName(tokenUserInfoDto.getNickName());
//        dto.setContactId(originalMessage.getContactId());
//        dto.setMessageType(MessageTypeEnum.WITHDRAW.getType());
//        dto.setSendTime(tipMessage.getSendTime());
//        dto.setContactType(originalMessage.getContactType());
//        dto.setStatus(MessageStatusEnum.SENDED.getStatus());
//        dto.setWithdrawMessageId(originalMessage.getMessageId());
//        dto.setIsWithdrawTip(true);
//
//        // 扩展数据中可以携带原消息信息，供前端使用
//        Map<String, Object> extendData = new HashMap<>();
//        extendData.put("originalMessageId", originalMessage.getMessageId());
//        extendData.put("withdrawTime", System.currentTimeMillis());
//        dto.setExtendData(extendData);
//
//        return dto;
//    }

}